/**
 * 生成master和game进程的代码逻辑
 * 和lua脚本层对接，实现和脚本层的通信
 * 给脚本层提供底层api接口，实现和其他进程的通信
 */
#include <string.h>
#include "global.h"
#include "my_signal.h"
#include "conf.h"
#include "work/workglobal.h"
#include "work/doubleBufQueue.h"
#include "work/exposeFunc.h"
#include "setproctitle.h"
#include "my_memory.h"

// main函数内部使用的函数的声明
static void FreeResource();
void TestDb();

// 全局变量声明
size_t g_argvneedmem; // 保存启动时argv参数所需要的内存大小
size_t g_envneedmem;  // 环境变量所占内存大小
int g_os_argc;        // argv参数个数
char **g_os_argv;     // 指向原始命令行参数数组，在main中赋值
char *gp_envmem;      // 指向自己分配的环境变量内存
int g_daemonized;     // 守护进程标记，是否启用（0不启用，1启用）
pid_t g_pid;          // 当前进程pid
int g_stopEvent;      // 程序退出标志（0退出）
int g_log;            // 全局日志fd
const char *g_title;  // 保存进程标题
// zmq对象声明
zmqLogic g_zmq0;       // master主 和 game  通过logic.ipc相连（PAIR→PAIR）
zmqLogic g_zmq1;       // 和log主	通过log.ipc相连（PUSH→PULL）
zmqLogic g_zmq2;       // 和db主 通过db.ipc相连（DEALER→ROUTER）
zmqLogic g_zmq3;       // 和net主 通过master.ipc 或game.ipc相连（PAIR→PAIR）
int g_procType;        // 保存进程类型（0：master； 1：game）
LuaManager g_luaMgr;   // 保存work全局Lua中间人对象
DBufQueue g_dBufQueue; // 初始化双缓冲区对象

int main(int argc, char *const *argv)
{
    if (argc < 2)
        return 1; // 参数不够直接退出
    int exitcode = 0;

    // 1.优先区分进程类型
    std::string procType = argv[1];
    if (procType == "master")
        g_procType = 0; // 标记为master进程
    else if (procType == "game")
        g_procType = 1; // 标记为game进程
    else
        return 1;

    // 2.加载配置表
    CConfig *p_config = CConfig::GetInstance();
    const char *name = (g_procType == 0) ? "master_log" : "game_log";
    if (p_config->Load("../all.conf") == false)
    {
        Log_init(name);
        Log_Error(0, "配置文件[%s]载入失败，退出!", "all.conf");
        exitcode = 1;
        goto lblexit;
    }

    // 3.初始化有必要的全局变量
    g_log = -1;
    g_stopEvent = 0;                                     // 标记程序当前不退出
    g_pid = getpid();                                    // 获取当前进程pid
    g_title = (g_procType == 0) ? "myMaster" : "myGame"; // 先给，以防配置表读不出来
    g_argvneedmem = 0;                                   // 保存启动时argv参数所需要的内存大小
    g_envneedmem = 0;                                    // 环境变量所占内存大小
    gp_envmem = NULL;                                    // 指向自己分配的环境变量内存
    g_daemonized = 0;                                    // 守护进程标记，是否启用（0不启用，1启用）
    Init_args(argc, argv);                               // 处理main函数传递的参数（放置到新内存，为设置标题做准备）
    CMemory::GetInstance();                              // 调用一下但单例类（初始化）
    g_dBufQueue.InitDbufQueue(HandleFunc);

    // 4.初始化日志系统和程序标题
    Log_init(name);
    Setproctitle(g_title); // 设置进程标题

    // 5.初始化信号
    if (Init_signals() != 0)
    { // 信号初始化
        Log_Error(0, "信号初始化失败！");
        exitcode = 1;
        goto lblexit;
    }

    // 6.初始化zmq消息队列
    if (Init_zmq() == false)
    {
        // 只要有一个不成功，就直接结束程序
        Log_Error(0, "ZMQ消息队列初始化失败！");
        exitcode = 1;
        goto lblexit;
    }
    std::this_thread::sleep_for(std::chrono::milliseconds(2000)); // 等待2秒让zmq完成连接

    // 8.测试数据库
    TestDb();
    Log_Info(0, "业务进程已经模拟发送给了数据库进程！！");

    // 7.加载注册函数并启动lua主脚本
    g_luaMgr.PutNewExposeFunc("_c_logic_send", _c_logic_send);
    g_luaMgr.PutNewExposeFunc("_c_net_send", _c_net_send);
    g_luaMgr.PutNewExposeFunc("_c_db_send", _c_db_send);
    g_luaMgr.PutNewExposeFunc("_c_log_send", _c_log_send);
    g_luaMgr.PutNewExposeFunc("_c_proc_type", _c_proc_type);
    if (g_luaMgr.Initialize() == false)
    {
        Log_Error(0, "注册或启动Lua脚本层失败！");
        exitcode = 1;
        goto lblexit;
    }

    // 8.进入主循环
    if (g_dBufQueue.BeginWorkCycle() == false)
    {
        Log_Error(0, "模块进入事件循环失败！");
        exitcode = 1;
        goto lblexit;
    }

// 能执行到这里说明系统要结束了
lblexit:
    Log_Info(0, "work进程退出了, byebye!");
    FreeResource(); // 垃圾回收
    return exitcode;
}

/**
 * 内部的资源回收函数
 */
static void FreeResource()
{
    // 清理环境变量新位置的内存
    if (gp_envmem)
    {
        delete[] gp_envmem;
        gp_envmem = NULL;
    }
    // 关闭日志文件
    if (g_log != STDERR_FILENO && g_log != -1)
    {
        close(g_log); // 不用判断结果了
        g_log = -1;   // 标记下，防止被再次close吧
    }
}

/**
 * 尝试发RPC给数据库模块
 */
void TestDb()
{
    g_zmq2.Send("REQ", ZMQ_SNDMORE);
    g_zmq2.Send("000001", ZMQ_SNDMORE);
    g_zmq2.Send("TestDb", ZMQ_SNDMORE);
    g_zmq2.Send("args", ZMQ_SNDMORE);
    g_zmq2.Send("127.0.0.1", 0);
}